//@(#) Log output stream (syslog(3))
//@(#) Author Andrew Wingorodov <http://andr.ru/>
//$Revision: 1.3 $

#ifndef _LOG_OSTREAM_H
#define _LOG_OSTREAM_H 1

#include <syslog.h>
#include <stdarg.h>

#include <ios>
#include <iostream>
#include <streambuf>

#include <boost/thread/mutex.hpp>

#ifndef SIZEBUF
#define SIZEBUF 1024
#endif

namespace log {

/** @class level
	@brief Set priority and level of messages
*/
struct level {

	//! \brief Priority of message
	enum pri {
		 emerg   = LOG_EMERG //!< A panic condition
		,alert   = LOG_ALERT //!< A condition that should be corrected
		,critical= LOG_CRIT //!< Critical condition, e.g, hard device error
		,error   = LOG_ERR //!< Errors
		,warning = LOG_WARNING //!< Warning messages
		,notice  = LOG_NOTICE //!< Possibly be handled specially
		,info    = LOG_INFO //!< Informational
		,debug   = LOG_DEBUG //!< For debugging program
	 };

	//! \brief Facility of message
	enum fac {
		 auth    = LOG_AUTH	//!< The authorization system
		,privat  = LOG_AUTHPRIV //!< The same as auth
		,console = LOG_CONSOLE //!< Messages written to /dev/console
		,cron    = LOG_CRON //!< The cron daemon
		,daemon  = LOG_DAEMON //!< Systems daemons
		,ftp     = LOG_FTP //!< The FTP daemon
		,kern    = LOG_KERN //!< Messages generated by the kernel
		,lpr     = LOG_LPR //!<  The line printer spooling
		,mail    = LOG_MAIL //!< The mail system
		,news    = LOG_NEWS //!< The network news
		,ntp     = LOG_NTP //!< The network time
		,security= LOG_SECURITY //!<  Security subsystems
		,syslog  = LOG_SYSLOG //!< Messages generated internally
		,user    = LOG_USER //!< By random user proccess, default facility
		,uucp    = LOG_UUCP //!< The uucp system
		,local0  = LOG_LOCAL0 //!< Reserved for local use
	};

	//! \brief Logging options
	enum opt {
		 cons    = LOG_CONS	//!< Write message to /dev/console
		,nodelay = LOG_NDELAY	//!< Open the connection to syslogd immediately
		,perror  = LOG_PERROR	//!< Write to standard error output
		,pid     = LOG_PID	//!< Log the proccess id with each message
	};

	//! \brief Priority of message
//@{
	int priority;
	int state;
//@}

	//! \brief Facility of message
	int facility;

	//! \brief Logging options
	int logopt;

	//! \brief Constructor
	inline level ()
		: priority (info)
		, state (info)
		, facility (user)
		, logopt (cons|nodelay|pid) {}
};

/** @class streambuf
	@brief Template of log stream buffer
*/
template <class C, class T = std::char_traits<C> >
class streambuf
	: public std::basic_streambuf<C,T>
{
typedef typename T::int_type int_type;
typedef typename T::pos_type pos_type;

public:
/*! \name Constructor and Destructor */
//@{
	//! \brief By default
	inline streambuf () : _is_open (false) { create_buffer (); }

	//! \brief Destructor
	inline ~streambuf () { destroy_buffer (); }
//@}

/*! \name Open and close */
//@{
	/** @brief Allocation memory and initialization buffer.
		@param ident_ Identification for openlog(3)
		@return Himself */
	inline streambuf* open (const char* ident_)
	{
		if (is_open()) close();
#ifndef WIN32
		::openlog (ident_, logopt(), facility());
#endif
		_is_open = true ;
		return this ;
	}

	//! \@brief Close buffer, free memory
	inline void close ()
	{
		if (!is_open()) return;
		sync ();
		this->setp (0,0);
		::closelog ();
		_is_open = false;
	}

	/** @brief Is open
		@return True */
	inline bool is_open () const { return _is_open; }
//@}

//! \name Priority and facility */
//@{
	inline int& state ()	{ return _level.state;    }
	inline int& priority ()	{ return _level.priority; }

	inline const int& logopt ()   const { return _level.logopt;   }
	inline const int& facility () const { return _level.facility; }
//@}

protected:
/*! \name Management of memory */
//@{
	//! \brief Create buffer
	inline void create_buffer ()
	{
		buffer = new C [SIZEBUF] ;
		this->setp (buffer, buffer+(SIZEBUF-1) );
	}

	//! \brief Delete buffer, memory free 
	inline void destroy_buffer ()
	{
		if (is_open()) close ();
		delete [] buffer ;
	}
//@}

//! \name Overload std::streambuf function */
//@{
	virtual int_type sync ();
	virtual int_type overflow (int_type c);
//@}

	//! \brief Level priority of message
	level _level;

private:
	streambuf (const streambuf&);
	streambuf& operator=(const streambuf&);

	C	*buffer;
	bool _is_open;
};

/** @class log_ios 
	@brief Base IOS of log::ostream
*/
template <class C, class T = std::char_traits<C> >
class log_ios
	: virtual public std::basic_ios<C,T>
{
protected:
/*! \name Constructor and Destructor */
//@{
	//! \brief Constructor by default
	inline log_ios ()
		: std::basic_ios<C,T> (0)
		, logbuf ()
	{
		this->init ( std::clog.rdbuf ()); }

	/** @brief Open to syslogd(3)
		@param ident_ Identification string for ::openlog(3) */
	inline explicit log_ios (const char* ident_)
		: std::basic_ios<C,T> (0)
		, logbuf () 
	{
		open (ident_); }

	//! \brief Destructor
	inline virtual ~log_ios () { this->close(); }
//@}

	//! \brief Work stream buffer
	log::streambuf<C,T> logbuf;

public:
	/** @brief Open log::ostream buffer
		@param ident_ Identification string for openlog(3) */
	inline void open (const char* ident_)
	{
		this->init (&logbuf);
		logbuf.open (ident_);
	}

	/** @brief Open log::ostream buffer
		@param ident_ Identification as std::string */
	inline void open (const std::string& ident_) { this->open(ident_.c_str()); }

	//! \brief Close log::stream buffer
	inline void close ()
	{
		logbuf.close ();
		this->init ( std::clog.rdbuf());
	}

	/** @brief Is open
		@return True */
	inline bool is_open () const { return logbuf.is_open (); }

	/** @brief Get level priority state
		@return State of level */
	inline int& state () { return logbuf.state (); }

	/** @brief Get level priority current
		@return Current priority of messages */
	inline int& priority () { return logbuf.priority (); }

	/** @brief Get stream buffer
		@return Pointer to the log::stream buffer */
	inline log::streambuf<C,T>* rdbuf () { return &logbuf; }
};

/** @class ostream 
	@brief Template for log output streams
*/
template <class C, class T = std::char_traits<C> >
class ostream
	: public std::basic_ostream<C,T>
	, public log_ios<C,T>
{
private:
	typedef std::basic_ostream<C,T> ostream_type;
	typedef log_ios<C,T> base_ios;

	using base_ios::logbuf;

public:
/*! \name Constructor and Destructor */
//@{
	//! \brief By default
	inline ostream () : ostream_type(0), base_ios () {}

	/** @brief Open to the syslogd
		@param ident_ Identification string to openlog(3) */
	inline explicit ostream (const char* ident_)
		: ostream_type(0)
		, base_ios (ident_) {}
//@}
	/**	@brief Set mask of priority
		@param priority_ Priority for messages */
	inline int level (int priority_) { return base_ios::state() = priority_; }

	/** @brief Set priority of messages
		@param pri_ Priority of message
		@return Himself */
	inline ostream& operator<<(const level::pri& pri_)
	{
		base_ios::priority() = pri_;
		return *this;
	}
};

//! \brief Chars log stream type
typedef log::ostream<char> logostream;

//! \brief The main global log stream
extern logostream logs;	//!< Global log stream

//! \brief The main global mutex for log stream
extern boost::mutex mutex;	//!< Safe threads
} //::log

#endif //!_LOG_OSTREAM_H
